﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Windows.Forms;
using KS.DataWrapper.DataWrapper.LINQ;
using KS.DataWrapper.Interface;

//http://www.k-press.ru/cs/2008/3/IQueryable/IQueryable.asp
//http://msdn.microsoft.com/ru-ru/library/bb546158.aspx
//http://blogs.msdn.com/b/mattwar/archive/2008/11/18/linq-links.aspx
//http://iqtoolkit.codeplex.com/
//https://expressiontree.codeplex.com/SourceControl/latest#ExpressionSerialization/DLinqSerializer.cs

namespace KS.DataWrapper.DataWrapper
{
    /// <summary>
    /// Объект кеша 
    /// </summary>
    /// <typeparam name="T">Тип объекта в коллекции</typeparam>
    public partial class DataCacheObject<T> : ISignalREventCommand where T : class, IDataClass
    {
        private readonly HttpClient httpClient;
        private readonly object synchronizer = new object();
        private readonly string cacheFilePath;
        private readonly string cacheFolderName = "Cache";
        private readonly string cacheFolderPath;
        private readonly ISignalRServerEvent signalR;
        private readonly bool useLocalCache;
        private ConcurrentDictionary<dynamic, T> nativeCollection;

        private string GetRequestUrl()
        {
            return string.Format("{0}/{1}", KS.DataWrapper.Config.WebApiConfig.BaseUrl, typeof(T).Name);
        }

        public Uri BaseAddress { get; set; }

        /// <summary>
        /// Контсруктор
        /// </summary>
        /// <param name="baseAddress">Базовый адресс</param>
        /// <param name="signalR">SignalR</param>
        /// <param name="useAsCache">использовать как кеш или просто, хранилище</param>
        public DataCacheObject(Uri baseAddress, ISignalRServerEvent signalR, bool useLocalCache = false)
        {
            BaseAddress = baseAddress;
            this.useLocalCache = useLocalCache;
            this.signalR = signalR;
            this.httpClient = new HttpClient(new HttpClientHandler
            {
                AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
            });
            this.httpClient.BaseAddress = BaseAddress;
            this.httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            this.nativeCollection = new ConcurrentDictionary<dynamic, T>();
            this.CacheFolder = string.Empty;
            this.cacheFilePath = string.Format("{0}\\{1}\\{2}.cache", Path.GetDirectoryName(Application.ExecutablePath), this.cacheFolderName, typeof(T).Name);
            this.cacheFolderPath = string.Format("{0}\\{1}", Path.GetDirectoryName(Application.ExecutablePath), this.cacheFolderName);
           // ServiceQuery = new Query<T>(new WebApiQueryProvider<T>(httpClient));
        }

        ~DataCacheObject()
        {
            this.SaveToFile();
            this.nativeCollection.Clear();
        }

       // public Query<T> ServiceQuery { get; set; }

        public IQueryable<T> ServiceQuery
        {
            get
            {
                return Mongo.MongoDBHelper.GetCollection<T>();
            }
            
        }
        

        public string CacheFolder { get; set; }

        #region Обновление коллеции с сервера


        /// <summary>
        /// Добавить элемент в коллекцию
        /// </summary>
        /// <param name="id">ID элемента</param>
        public  async Task AddToCollectionSignalREvent(List<string> id)
        {
             var item = await this.GetItemById(id.First());
             AddToNativeCollection(item);
        }

        

        /// <summary>
        /// Обновить элемент в коллекции
        /// </summary>
        /// <param name="id">ID элемента</param>
        public async Task UpdateCollectionSignalREvent(List<string> id)
        {
            var item = await this.GetItemById(id.First());
            UpdateNativeCollection(item);
        }


        /// <summary>
        /// Удалить элемент из коллекции
        /// </summary>
        /// <param name="id">ID элемента</param>
        public void RemoveFromCollectionSignalREvent(List<string> id)
        {
            foreach (var item in id)
            {
                RemoveFromNativeCollectionById(item);
            }
        }

        
        
        #endregion
        
        //#region LINQ
        ///// <summary>
        ///// LINQ запрос к данным
        ///// </summary>
        ///// <param name="expression">Услвие</param>
        ///// <param name="skip">пропустить</param>
        ///// <param name="take">взять</param>
        ///// <returns>Коллекция с элементами</returns>
        //public async Task<IEnumerable<T>> GetQuery(Expression<Func<T, bool>> expression = null, int skip = 0, int take = 0)
        //{
        //    try
        //    {
        //        var context = new DataServiceContext(this.httpClient.BaseAddress);
        //        var query = (IQueryable<T>)context.CreateQuery<T>(this.GetRequestUrl());
        //        if (expression != null)
        //        {
        //            query = query.Where(expression);
        //        }
        //        if (skip != 0)
        //        {
        //            query = query.Skip(skip);
        //        }
        //        if (take != 0)
        //        {
        //            query = query.Take(take);
        //        }
        //        var uri = new Uri(((DataServiceQuery)query).RequestUri.ToString().Replace("()?$", "?$"));
        //        HttpResponseMessage response = await this.httpClient.GetAsync(uri);
        //        response.EnsureSuccessStatusCode(); // Throw on error code.
        //        var itemList = await response.Content.ReadAsAsync<IEnumerable<T>>();
        //        return itemList;
        //    }
        //    catch
        //    {
        //        return null;
        //    }
        //}
        ///// <summary>
        ///// Очистить коллекцию и добавить результаты запроса
        ///// </summary>
        ///// <param name="expression">LINQ условие</param>
        //public async Task ClearAndInserQuery(Expression<Func<T, bool>> expression = null)
        //{
        //    try
        //    {
        //        //this.SafeClear();
        //        var context = new DataServiceContext(this.httpClient.BaseAddress);
        //        var query = (IQueryable<T>)context.CreateQuery<T>(this.GetRequestUrl());
        //        if (expression != null)
        //        {
        //            query = query.Where(expression);
        //        }
        //        var uri = new Uri(((DataServiceQuery)query).RequestUri.ToString().Replace("()?$", "?$"));
        //        HttpResponseMessage response = await this.httpClient.GetAsync(uri);
        //        response.EnsureSuccessStatusCode(); // Throw on error code.
        //        var itemList = await response.Content.ReadAsAsync<IEnumerable<T>>();
        //        foreach (var item in itemList)
        //        {
        //            //this.Create(item);
        //        }
        //    }
        //    catch
        //    {
        //    }
        //}
        ////public async Task<T> GetQueryLastOrDefault(Expression<Func<T, bool>> expression = null)
        ////{
        ////    var context = new DataServiceContext(this.httpClient.BaseAddress);
        ////    var query = (IQueryable<T>)context.CreateQuery<T>(this.GetRequestUrl());
        ////    if (expression != null)
        ////    {
        ////        query = query.Where(expression);
        ////    }
        ////    query = query.LastOrDefault()
        ////    HttpResponseMessage response = await this.httpClient.GetAsync(uri);
        ////    response.EnsureSuccessStatusCode(); // Throw on error code.
        ////    var itemList = await response.Content.ReadAsAsync<IEnumerable<T>>();
        ////    return itemList;
        ////}
        //#endregion
    }
}